package org.mmtk.policy.compressor;


import org.mmtk.plan.TransitiveClosure;
import org.mmtk.plan.compressor.Compressor;
import org.mmtk.plan.compressor.CompressorCollector;
import org.mmtk.policy.Space;
import org.mmtk.utility.Log;

import org.vmmagic.pragma.Uninterruptible;
import org.vmmagic.unboxed.Address;

import org.vmmagic.unboxed.ObjectReference;

@Uninterruptible
public class ReferenceFixer extends TransitiveClosure {
  
  private CompressorCollector collector;
  private static boolean VERBOSE = false;
  
  public ReferenceFixer(CompressorCollector compressorCollector) {
    this.collector = compressorCollector;
  }
  
  public ObjectReference getNewReference(ObjectReference object) {
    if (object.isNull()) {
      return object;
    }
    
    if (Space.isInSpace(Compressor.MC0, object)) {
      return Compressor.markCopySpace0.getNewReference(this, object);
    }
    if (Space.isInSpace(Compressor.MC1, object)) {
      return Compressor.markCopySpace1.getNewReference(this, object);
    }
    if(VERBOSE) {
      Log.write("[getNewReference] Reference outside spaces, returning original");
    }
    return object; 
  }
  
  /**
   * Trace an edge during GC. 
   * Find the new address of the pointed to object using auxiliary metadata 
   * and store the new reference in the slot.
   *
   * @param source The source of the reference.
   * @param slot The location containing the object reference.
   */
  public void processEdge(ObjectReference source, Address slot) {
    ObjectReference object = slot.loadObjectReference();
    if(VERBOSE) {
      Log.write("[processEdge] Slot[ "); Log.write(slot); Log.write("] ");
      Log.write(" Obj["); Log.write(object); Log.write("] ");
    }
    
    ObjectReference newRef = getNewReference(object);
    if(VERBOSE) {
      Log.write("NewRef["); Log.write(newRef); Log.write("]");
      Log.writeln();
    }
    slot.store(newRef);  
  }
  
  public CompressorCollector getCollector() {
    return collector;
  }
  
  
}
  
  
//  public void processEdge(ObjectReference source, Address slot) {
//    ObjectReference object = slot.loadObjectReference();  
//    
//    if (!object.isNull()) {
//      Address objAddr = object.toAddress();
//      if (VM.VERIFY_ASSERTIONS && Options.verbose.getValue() >= 9) {
//        Log.write("OffsetVector for "); Log.write(object);
//        Log.write(" In "); Log.write(CompressorMetadata.getOffsetVectorCellAddress(objAddr));
//        Log.writeln();
//      }
//      
//      int blockOffset = CompressorMetadata.getOffsetVectorCellAddress(objAddr).loadInt();
//      
//      if (VM.VERIFY_ASSERTIONS && Options.verbose.getValue() >= 9) {
//        Log.write("blockOffset "); Log.write(blockOffset); Log.writeln();
//      }
//      
//      int liveDataInBlock = Block.liveData(Block.align(objAddr), objAddr);
//      Extent offsetInBlock = Extent.fromIntZeroExtend(liveDataInBlock);
//      
//      //TODO: what is the base address in the to-space ?
//      Address base = collector.getBase();
//      
//      if(VM.VERIFY_ASSERTIONS) {
//        VM.assertions._assert(!collector.getBase().isZero());
//      }
//      
//      Extent offset = offsetInBlock.plus(blockOffset);
//      Address newAddr = base.plus(offset);
//      
//      ObjectReference newRef = VM.objectModel.getReferenceWhenCopiedTo(object, newAddr);
//      
//      if (VM.VERIFY_ASSERTIONS && Options.verbose.getValue() >= 9) {
//        Log.write("Old address: "); Log.write(object.toAddress()); Log.writeln();
//        Log.write("New address: "); Log.write(newRef); Log.writeln();
//      }
//      
//      slot.store(newRef);
//    }
//  }
